package org.pyframe.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


@SuppressWarnings({"rawtypes", "unchecked"})
public class Packet {
	protected Packet packet;
	
	List parts = new ArrayList();
	HashMap partMap = new HashMap();

	public void addPart(Part part) {
		parts.add(part);
		if (partMap.containsKey(part.getName())) {
			throw new RuntimeException("Can't add same part name : ["+part.getName()+"]");
		}
		partMap.put(part.getName(), part);
	}
	
	public Part getPart(int index) {
		return (Part) parts.get(index);
	}
	
	public Part getPart(String name) {
		for (int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			if(name.equals(part.getName())) {
				return part;
			}
		}
		throw new RuntimeException("Can't find part name : ["+name+"]");
	}
	
	public void setValue(String name, String value) {
		if(!partMap.containsKey(name)) {
			throw new RuntimeException("Can't find part name : ["+name+"]");
		}
		Part part = (Part) partMap.get(name);
		part.setValue(value);
	}
	
	public void setArrValue(String name, List<String> value) {
		Part part = (Part) partMap.get(name);
		part.setArrValue(value);
	}
	
	public String getValue(String name) {
		if (partMap.containsKey(name)) {
			Part part = (Part) partMap.get(name);
			if(part.getType()!=null && !"N".equals(part.getType()) && !"C".equals(part.getType())) {
				throw new RuntimeException("Array part does not support getValue, use getList instead.");
			}
			
			String result = part.getValue();
			if(result==null) return result;
			return result.trim();
		}else {
			return "";
		}
	}
	
	public List<String> getList(String name) {
		if (partMap.containsKey(name)) {
			Part part = (Part) partMap.get(name);
			return part.getArrValue();
		}else {
			return null;
		}
	}
	
	public int size() {
		int result = 0;
		for(int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			result += part.getLength();
		}
		return result;
	}
	
	public String setPartValue(byte[] data, Part part, int pos, boolean isArray) {
		byte[] t = new byte[part.getLength()];
		try {
			System.arraycopy(data, pos, t, 0, part.getLength());
		}catch(java.lang.ArrayIndexOutOfBoundsException e) {
			System.err.println("parse error : "+part.toString());
			throw new RuntimeException(e);
		}
		String temp = new String(t);
		if(isArray) {
			part.getArrValue().add(temp.trim());
		}else {
			if (part.isEbcdic()) {
				try {
					if(part.isPack()) {
						part.setValue(""+PackedDecimal.parse(t));
					}else {
						part.setValue(new String(t, "CP933"));
					}
				}catch(Exception e) {
//					logger.error("", e);
					part.setValue("");
				}
			} else {
				part.setValue(temp);			
			}
		}
		return temp;
	}
	
	public byte[] parse(byte[] data) {
		int pos = 0;
		int count = 0;
		
		for(int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			
			/*
			 * normal type 'N' or Null
			 */
			if("".equals(PacketTools.nvl(part.getType(), "")) || "N".equals(part.getType())) {
				setPartValue(data, part, pos, false);
				pos += part.getLength();
			}
			
			/*
			 * count type 'C'
			 */
			else if("C".equals(part.getType())) {
				count = Integer.parseInt(setPartValue(data, part, pos, false));
				pos += part.getLength();
			}			
			
			/*
			 * Array type 'A'
			 */
			else if ("A".equals(part.getType())) {
				int next = 0;
				while(count > 0) {
					if ((i+next) >= parts.size()) {
						count--;
						next = 0;
						continue;
					}
					
					Part arrPart = (Part) parts.get(i+ next);
					if(!"A".equals(arrPart.getType())) {
						count--;
						next = 0;
						continue;
					}
					
					setPartValue(data, arrPart, pos, true);
					pos += arrPart.getLength();
					next++;
				}
			}
		}
		
		byte[] rest = new byte[data.length-pos];
		System.arraycopy(data, pos, rest, 0, data.length-pos);
		return rest;
	}
	
	public String toString() {
		StringBuffer s = new StringBuffer();
		for(int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			s.append(part.toString());
			s.append("\n");
		}
		return s.toString();
	}
	
	private String getArrayRaw(int cnt) {
		StringBuffer s = new StringBuffer();
		for (int j=0; j<cnt; j++) {
			for(int i=0; i<parts.size(); i++) {
				Part part = (Part) parts.get(i);
				if(!"A".equals(part.getType())) {
					continue;
				}
				if(part.getArrValue().size()==0) {
					s.append(part.raw(""));
				}else {
					String value = (String) part.getArrValue().get(j);
					s.append(part.raw(value));
				}
			}
		}
		return s.toString();
	}
	
	public String raw() {
		StringBuffer s = new StringBuffer();
		
		int cnt = 0;
		boolean isArrayStart = false;
		for(int i=0; i<parts.size(); i++) {
			
			Part part = (Part) parts.get(i);
			if ("C".equals(part.getType())) {
				if("".equals(part.getValue())) {
					part.setValue("0");
				}
				cnt = Integer.parseInt(part.getValue());
			}
			
			if (!isArrayStart && "A".equals(part.getType())) {
				isArrayStart = true;
				s.append(getArrayRaw(cnt));
			}
			
			if ("A".equals(part.getType())) {
				continue;
			}
			
			s.append(part.raw());
		}
		return s.toString();
	}
	
	public void parseD(String data) {		
		String[] elements = data.split("\\x1A", -1);
		if (elements.length < parts.size()) {
			throw new RuntimeException("Parsing information is not correct. : ["+data+"]");
		}
		
		int count = 0;
		for(int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			
			/*
			 * normal type 'N' or Null
			 */
			if("".equals(PacketTools.nvl(part.getType(), "")) || "N".equals(part.getType())) {
				part.setValue(elements[i]);
				count = 0;
			}
			
			/*
			 * count type 'C'
			 */
			else if("C".equals(part.getType())) {
				String t = elements[i];
				if ("".equals(t)) {
					t = "0";
				}
				count = Integer.parseInt(t);
				part.setValue(elements[i]);
			}
			
			/*
			 * Array type 'A'
			 */
			else if ("A".equals(part.getType())) {
				List arrValue = new ArrayList();
				part.setArrValue(arrValue);
				for(int j=0; j<count; j++) {
					arrValue.add("");
				}
				String[] arr = elements[i].split("\\x1B", -1);
				for(int j=0; j<arr.length; j++) {
					arrValue.set(j, arr[j]);
				}
			}
		}
	}
	
	public String rawD() {
		return join((char)0x1A, (char)0x1B);
	}
	
	public String join(char c1, char c2) {
		StringBuffer result = new StringBuffer();
		for(int i=0; i<parts.size(); i++) {
			Part part = (Part) parts.get(i);
			if(part.getType()==null || "N".equals(part.getType())) {
					result.append(part.getValue());
			} else if("C".equals(part.getType())) {
					result.append(part.getValue());				
			} else if("A".equals(part.getType())) {
				List t = part.getArrValue();
				for(int j=0; j<t.size(); j++) {
					String repeat = (String) t.get(j);
					result.append(repeat);
					if(j<t.size()-1) {
						result.append(c2);
					}
				}
			}
			if(i<parts.size()-1) {
				result.append(c1);
			}
		}
		return result.toString();
	}
}
